/**
 * Copyright (c) 2008 - TheNittyGritty - Thomas Brekelmans
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package nl.thenittygritty.utils 
{

	/**
	 * NumberUtils is a library class which shouldn't be instantiated. 
	 * Instead it provides static methods which help in working with Numbers.
	 */
	public class NumberUtils 
	{
		/**
		 * Private constructor for the NumberUtils, this shouldn't be called.
		 */
		public function NumberUtils() 
		{
			// Throw an error here, we shouldn't try to instantiate NumberUtils.
			throw new Error("The NumberUtils shouldn't be instantiated.");
		}
		
		/**
		 * Creates a random number within a given range.
		 */
		public static function getRandomNumberInRange(rangeStart:Number,
													  rangeEnd:Number):Number 
		{
			var rangeSize:Number = rangeEnd - rangeStart;
			return rangeStart + (rangeSize - Math.random() * rangeSize);
		}
		
		/**
		 * Rounds a floating point number to a given precision.
		 * 
		 * @param precision This can be any positive number, where 0.1 means the 
		 * value is rounded on 1 decimal, 0.01 means it's rounded on tenths 
		 * of decimals, and 5 means it's rounded to the nearest number 
		 * dividable by 5 (13 becomes 15 and 12 becomes 10).
		 */
		public static function roundFloat(value:Number, 
										  precision:Number = 1):Number 
		{
			if (precision < 0) 
			{
				precision = -precision;
			}
			return Math.round(value / precision) * precision;
		}
		
		/**
		 * Ceils a floating point number to a given precision.
		 * 
		 * @param precision This can be any positive number, where 0.1 means 
		 * the value is rounded on 1 decimal, 0.01 means it's rounded on tenths 
		 * of decimals, and 5 means it's rounded to the nearest number 
		 * dividable by 5 (13 becomes 15 and 12 becomes 10).
		 */
		public static function ceilFloat(value:Number, 
										 precision:Number = 1):Number 
		{
			if (precision < 0) 
			{
				precision = -precision;
			}
			return Math.ceil(value / precision) * precision;
		}
		
		/**
		 * Floors a floating point number to a given precision.
		 * 
		 * @param precision This can be any positive number, where 0.1 means 
		 * the value is rounded on 1 decimal, 0.01 means it's rounded on tenths 
		 * of decimals, and 5 means it's rounded to the nearest number 
		 * dividable by 5 (13 becomes 15 and 12 becomes 10).
		 */
		public static function floorFloat(value:Number, 
										  precision:Number = 1):Number 
		{
			if (precision < 0) 
			{
				precision = -precision;
			}
			return Math.round(value / precision) * precision;
		}

		/**
		 * Finds the x value of a point on a sine curve of which only 
		 * the y value is known. The closest x value is returned, 
		 * ranging between -1 pi and 1 pi.
		 */
		public static function xPosOnSinus(yPosOnCurve:Number, 
										   curveBottom:Number, 
										   curveTop:Number):Number 
		{
			return Math.asin(2 * NumberUtils.getNormalizedValue(yPosOnCurve, 
													curveBottom, curveTop) - 1);
		}

		/**
		 * Finds the relative position of a number in a range between min and 
		 * max, and returns its normalized value between 0 and 1.
		 */
		public static function getNormalizedValue(value:Number, min:Number, 
												  max:Number):Number 
		{
			var difference:Number = max - min;
			
			if (difference == 0) 
			{
				return min;
			}
			
			var normalizeFactor:Number = 1 / difference;
			return normalizeFactor * (value - min);
		}

		/**
		 * Calculates the value of a continuum between start and end given a 
		 * percentage position.
		 * 
		 * @param percentage This is a percentage value where 1 means 100%.
		 */
		public static function getPercentageValue(percentage:Number, min:Number, 
												  max:Number):Number 
		{
			return min + (percentage * (max - min));
		}

		/**
		 * Calculates the angle of a vector in degrees (if radians are desired, 
		 * pass false as an optional third parameter).
		 */
		public static function getAngle(deltaX:Number, deltaY:Number, 
										useDegrees:Boolean = true):Number 
		{
			var result:Number = Math.atan2(deltaY, deltaX);
			if (useDegrees)
			{
				result = NumberUtils.toDegrees(result);
			}
			return result;
		}

		/**
		 * Converts a value in radians to degrees.
		 */
		public static function toDegrees(value:Number):Number
		{
			return value * 180 / Math.PI;	
		}

		/**
		 * Converts a value in degrees to radians.
		 */
		public static function toRadians(value:Number):Number
		{
			return value * Math.PI / 180;	
		}
	}
}
